#lang sicp
(define (deriv exp var)
  (cond ((number? exp) 0)
        ((variable? exp)
         (if (same-variable? exp var) 1 0))
        ((sum? exp)
         (make-sum (deriv (addend exp) var)
                   (deriv (augend exp) var)))
        ((product? exp)
         (make-sum (make-product (multiplier exp)
                                 (deriv (multiplicand exp) var))
                   (make-product (multiplicand exp)
                                 (deriv (multiplier exp) var))))
        ((exponentiation? exp)
         (make-product (exponent exp)
                       (make-product (make-exponentiation (base exp)
                                                          (make-subtract (exponent exp) 1))
                                     (deriv (base exp) var))))
        (else
         (error "unkown expression!" exp var))))

(define (variable? exp) (symbol? exp))
(define (same-variable? exp var) (and (variable? exp)
                                      (variable? var)
                                      (eq? exp var)))

(define (make-sum a b)
  (cond ((and (number? a) (number? b))
         (+ a b))
        ((=number? a 0) b)
        ((=number? b 0) a)
        (else (list a '+ b))))
(define (addend sum) (list->exp (list-before-symbol '+ sum)))
(define (augend sum) (list->exp (list-behind-symbol '+ sum)))
(define (list->exp se)
  (if (null? (cdr se))
      (car se)
      se))
(define (list->sum se)
  (if (null? (cdr se))
      (car se)
      (cons '+ se)))
(define (sum? exp) (memq '+ exp))
      

(define (list-before-symbol s se)
  (define (iter cur res)
    (cond ((null? cur) #f)
          ((eq? (car cur) s) res)
          (else (iter (cdr cur) (cons (car cur) res)))))
  (let ((res (iter se nil)))
    (if res
        (reverse res)
        #f)))
(define (list-behind-symbol s se)
  (let ((res (memq s se)))
    (if res
        (cdr res)
        #F)))

(define (make-product a b)
  (cond ((or (=number? a 0) (=number? b 0)) 0)
        ((and (number? a) (number? b)) (* a b))
        ((=number? a 1) b)
        ((=number? b 1) a)
        (else (list a '* b))))
(define (multiplier product) (list->exp (list-before-symbol '* product)))
(define (multiplicand product) (list->exp (list-behind-symbol '* product)))
(define (list->product se)
  (if (null? (cdr se))
      (car se)
      (cons '* se)))
(define (product? exp) (memq '* exp))

(define (=number? exp n) (and (number? exp) (number? n) (= exp n)))

(define (make-exponentiation base exponent)
  (cond ((=number? exponent 1) base)
        ((=number? exponent 0) 1)
        (else (list base '** exponent))))
(define (base exponentiation) (list->exp (list-before-symbol '** exponentiation)))
(define (exponent exponentiation) (list->exp (list-behind-symbol '** exponentiation)))
(define (exponentiation? exp) (memq '** exp))

(define (make-subtract a b)
  (cond ((and (number? a) (number? b)) (- a b))
        ((=number? b 0) a)
        (else (list '- a b))))
